interface Golang的哲学变革
If I could export one feature of Go into other languages, it would be interfaces. ———Rob Pike
interface作为Golang的整个类型系统的基石,让Golang在基础编程哲学的探索上达到前所未有的高度。> ——《Go语言编程》
接口是Java引入的特性。在软件工程上占有重要地位。而Golang变革了接口的概念。
我们考虑这样一个情况,有两个struct都实现了一些非常有用的小子集中的相关方法,这时有办法操作两个struct的任意一个就显得非常有用。那么我们该怎么办呢?这就是接口的作用。
type s1 struct{
...//一些属性
}
type s2 struct{
...//一些属性
}
func (a *s1)foo(){
...//具体实现1
}
func (b *s2)foo(){
...//具体实现2
}
这时,如果我们有需要操作两个stuct中的任意一个,可以声明这样一个interface
type Foo interface{
foo()
//只要实现了foo()函数,就相当于实现了Foo接口
//我们就可以用接口来调用对象
}
func test(a Foo,b int){
a.foo()
//传进不同的struct对象,函数将调用不同的foo()方法
}
从这个例子,我们就能看出interface的作用,降低耦合性,可以让某个模块或功能重复利用。
我们来看看io包,其中最基本的也是最重要的两个接口Reader和Writer。我们说,只要实现了这两个接口,就基本拥有了IO的功能。
func ReadFrom(reader io.Reader,num int)([]byte,error){
p := make([]byte,num)
n,err:= reader.Read(p)
if n > 0 {
return p[:n],nill
}
return p,err
}
ReadFrom将io.Reader作为参数,我们可以从任何实现了io.Reader接口的地方读取数据
//从标准输入流读取
data,err := ReadFrom(os.Stdin,10)
//从文件中读取
file, err1 := os.Open(util.GetProjectRoot() + "01.txt")
data,err2 := ReadFrom(file,10)
//从字符串读取
data,err := ReadFrom(strings.NewReader("from string"),10)
这是strings包中关于Reader的Read的实现
func (r *Reader) Read(b []byte) (n int, err error) {
if len(b) == 0 {
return 0, nil
}
if r.i >= len(r.s) {
return 0, io.EOF
}
n = copy(b, r.s[r.i:])
r.i += n
r.prevRune = -1
return
}
这里没有强制声明strings.Reader这个struct实现了哪个interface,但是它实现了Read方法,在Golang中也就实现了io.Reader这个接口。这种隐式的声明在Golang中术语叫做非侵入式接口。这样做的好处,我曾经在 初识Go语言 中提过。
其一,Go语言的标准库,再也不需要绘制类库的继承树图。
其二,实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划。
其三,不用为了实现一个接口而导入一个包,因为多引用一个外部的包,就意味着更多的耦合。接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口。
Golang也可以通过接口的组合,实现一种抽向上的层次,
type ReadWriter interface {
Reader
Writer
}
type ReadCloser interface {
Reader
Closer
}
type WriteCloser interface {
Writer
Closer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
}
这几个接口都是Reader,Writer,Closer等基本接口的组合,可以说接口组合为Go程序建立起一个严密而有序的体系。
最后再谈谈一个有趣的interface ——interface{} 由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可以指向任何对象的Any类型。
var v1 interface{} = 1 // 将int类型赋值给interface{}
var v2 interface{} = "abc" // 将string类型赋值给interface{}
var v3 interface{} = &v2 // 将*interface{}类型赋值给interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标准库fmt中PrintXXX系列的函数,例如:
func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
####总结
interface作为Golang类型系统的基石,为Golang工程形成了一个严密而清晰的体系,但其理念和使用却极为简洁,是Golang众多优秀特性中极为突出的一个,是我们学习Golang必须掌握的一点。
[返回顶部]()
blog comments powered by Disqus